/* Lighting */
uniform extern float4 gAmbientLight;
uniform extern float4 gDiffuseLight;
uniform extern float4 gSpecularLight;
uniform extern float3 gLightPosition; //world space
uniform extern float3 gEyePosW;
uniform extern float2 gLightPos; //Screen space

/* Transforming */
uniform extern float4x4 g_mWorld;
uniform extern float4x4 g_mView;
uniform extern float4x4 g_mProj;

/* Shadows */
uniform extern float4x4 g_mViewToLightProj;  // Transform from view space to light projection space
uniform extern float3   g_vLightDir;		// Light direction in view space
uniform extern float    g_fCosTheta;		// Cosine of theta of the spot light
uniform extern bool		g_isMapped;			//so we can turn shadow mapping on or off
uniform extern float    gVal;				//rain translation
uniform extern int      g_mPasses;

/* Audio */
uniform extern float3   g_audioDebugColorMul;
uniform extern float    g_audioDebugAlpha;

/* God rays */
uniform extern float3 gGlobalClearColor;
uniform extern float gTHold;
uniform extern float gDensity;
uniform extern float gIllumDecay;
uniform extern float gWeight;
uniform extern float gDecay;
uniform extern float gExposure;


/* Defines for the Shadow calculations */
#define SMAP_SIZE 2048
#define NOISE_AMOUNT 0.00008f

/* Used when rendering meshes or full screens */
texture Diffuse;
sampler DiffuseSampler = sampler_state
{
    Texture = <Diffuse>;
	AddressU = Clamp;
    AddressV = Clamp;
};

/* Shadows */
texture ShadowMap;
sampler ShadowSampler = sampler_state
{
    Texture = <ShadowMap>;
	MinFilter = Point;
    MagFilter = Point;
    MipFilter = Point;
    AddressU = Clamp;
    AddressV = Clamp;
};

/* God Ray Textures */
texture LightTexture;
sampler LightTextureSampler = sampler_state
{
    Texture = <LightTexture>;
	AddressU = Clamp;
    AddressV = Clamp;
};

texture DarkPassTexture;
sampler DarkPassSampler = sampler_state
{
    Texture = <DarkPassTexture>;
	AddressU = Clamp;
    AddressV = Clamp;
};

texture DownSizeThis;
sampler DownSizeSampler = sampler_state
{
    Texture = <DownSizeThis>;
	AddressU = Clamp;
    AddressV = Clamp;
};

texture GodRayThis;
sampler GodRaySampler = sampler_state
{
    Texture = <GodRayThis>;
	AddressU = Clamp;
    AddressV = Clamp;
};

texture Blend1;
sampler Blend1Sampler = sampler_state
{
    Texture = <Blend1>;
	AddressU = Clamp;
    AddressV = Clamp;
};
texture Blend2;
sampler Blend2Sampler = sampler_state
{
    Texture = <Blend2>;
	AddressU = Clamp;
    AddressV = Clamp;
};
texture LightAdded;
sampler LightSampler = sampler_state
{
    Texture = <LightAdded>;
	AddressU = Clamp;
    AddressV = Clamp;
};
texture LightSource;
sampler LightSourceSampler = sampler_state
{
    Texture = <LightSource>;
	AddressU = Clamp;
    AddressV = Clamp;
};
texture RainSource;
sampler RainSourceSampler = sampler_state
{
    Texture = <RainSource>;
	AddressU = Clamp;
    AddressV = Clamp;
};
texture RainTexture;
sampler RainSampler = sampler_state
{
    Texture = <RainTexture>;
	AddressU = Wrap;
    AddressV = Wrap;
};
texture OrigionalLight;
sampler OrigionalLightSampler = sampler_state
{
    Texture = <OrigionalLight>;
	AddressU = Wrap;
    AddressV = Wrap;
};

/* Normal vertexes */
struct InputVS
{
    float4 pos : POSITION0;
    float4 norm: NORMAL0;
    float2 uv: TEXCOORD0;
 };

struct OutputVS
{
    float4 posH : POSITION0;
    float4 posW : TEXCOORD3;
    float2 UV0 : TEXCOORD0;
    float3 normalW : TEXCOORD1;
};

//Special ShadowVS return value 
struct OutShadowMap
{
    float4 posH : POSITION0;
    float4 posW : TEXCOORD3;
	float4 posL : TEXCOORD4;
    float2 UV0 : TEXCOORD0;
    float3 normalW : TEXCOORD1;
};
/* HELPER FUNCTIONS */

float2 ClampScreen(float2 pos)
{ 	
	/* X: 0 ------------ 1
	 * Y:  ________________
	 * 0  |                |
	 *    |                | 
	 *    |                |
	 * 1  |________________|*/ 
	float2 screenPos;
	screenPos.x = (pos.x / 2) + 0.5f;
	screenPos.y = 1 -( (pos.y * 0.5f) + 0.5f);
	return screenPos;
};

/************************************************************************/
/* Texture: Simply transforms the vertex, and renders the textured 
   triangle using DiffuseSampler. No lighting calculations are performed.
   USES: SkyBox, Debugging                                              */
/************************************************************************/
OutputVS TextureVS(InputVS input);
float4 TexturePS(OutputVS input);
technique Texture
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 TextureVS();
        PixelShader = compile ps_2_0 TexturePS();
    }
}
OutputVS TextureVS(InputVS input)
{
	//0 the output,
    OutputVS output = (OutputVS)0;
	float4x4 worldView = mul( g_mWorld,  g_mView);
	//tranform posH for use by the GPU
	output.posH = mul(input.pos, mul(worldView,g_mProj));
	//propogate the UV's through to the PixelShader
    output.UV0 = input.uv;
    return output;
}
//simply return the color on the texture
float4 TexturePS(OutputVS input) : COLOR0
{
	float4 col = tex2D(DiffuseSampler,input.UV0).rgba;
	return  col;
}
/************************************************************************/
/*  Vertex Shader Output: OutPutWScreenPos
	Uses: Used to propgate the homogenous coordinates of a vertex
	through to the pixel shader, as posH is used up by the GPU.
	 
	screenPos can be passed into ClampScreen() to work out the coordinate
	of this pixel on the screen                                         */
/************************************************************************/
struct OutPutWScreenPos
{
	float4 posH : POSITION0;
    float4 posW : TEXCOORD3;
    float2 UV0 : TEXCOORD0;
    float3 normalW : TEXCOORD1;
	float4 screenPos : TEXCOORD2;
};
/************************************************************************/
/* DarkBlend: Uses two textures: DarkPassSampler, and LightTextureSampler
   and adds them together. Uses the gGlobalClearColor variable.
   
   Uses: Adds the silluette dark pass to the origional light texture,
   ensures scenery blocks sunlight							            */
/************************************************************************/
OutPutWScreenPos BlendVS(InputVS input);
float4 BlendPS(OutPutWScreenPos input);
technique DarkBlend
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 BlendVS();
        PixelShader = compile ps_2_0 BlendPS();
    }

}
OutPutWScreenPos BlendVS(InputVS input)
{
	//0 the output
    OutPutWScreenPos output = (OutPutWScreenPos)0;
	// transforms the vertex into homogenous space
	float4x4 worldView = mul( g_mWorld,  g_mView);
    output.posH =  mul(input.pos, mul(worldView, g_mProj));
	//propogate the UV's and position through
    output.UV0 = input.uv;
	output.screenPos = output.posH;
    return output;
}
float4 BlendPS(OutPutWScreenPos input) : COLOR0
{
	//calculate which pixel we are on
	float2 screenPos = ClampScreen(input.screenPos.xy);
	//if this is NOT white on the silluette texture, return it
    float4 col0 = tex2D(DarkPassSampler,screenPos).rgba;
	if(	col0.r  != gGlobalClearColor.r  ||
		col0.g  != gGlobalClearColor.g  ||
		col0.b  != gGlobalClearColor.b)
		return col0;//always black
	
	//other whys, this pixel was white, and therefore not part of the scenery, 
	//so we use the generated sun texture
	return tex2D(LightTextureSampler,screenPos).rgba;
}

/************************************************************************/
/* Game Specific Data                                                   */
/************************************************************************/

OutPutWScreenPos LightSampleVS(InputVS input);
float4 LightSamplePS(OutPutWScreenPos input);
technique LightSample
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 LightSampleVS();
        PixelShader = compile ps_2_0 LightSamplePS();
    }
}
//blending VERTEX
OutPutWScreenPos LightSampleVS(InputVS input)
{
    OutPutWScreenPos output = (OutPutWScreenPos)0;
	float4x4 worldView = mul( g_mWorld,  g_mView);
    output.posH =  mul(input.pos, mul(worldView, g_mProj));
    output.UV0 = input.uv;
	output.screenPos = output.posH;
    return output;
}


//blending PIXEL
float4 LightSamplePS(OutPutWScreenPos input) : COLOR0
{
	float2 screenPos = ClampScreen(input.screenPos.xy);
    float4 col0 = tex2D(LightSampler,screenPos).rgba;
	float4 col1 = tex2D(LightSourceSampler,screenPos).rgba;
	float4 comp = col0 + col1;
	if(	col0.r  == comp.r &&
		col0.g  == comp.g &&
		col0.b  == comp.b)
		return col0;
	
	return float4(0,0,0,1);
}
//dark pass VERTEX
OutputVS DarkPassVS(InputVS input)
{
    OutputVS output = (OutputVS)0;
	float4x4 worldView = mul( g_mWorld,  g_mView);
    output.posH =  mul(input.pos, mul(worldView, g_mProj));
    output.posW = mul(input.pos, worldView);
    return output;
}


//dark pass PIXEL
float4 DarkPassPS(OutputVS input) : COLOR0
{
	return float4 (0,0,0,1);	
}

technique DarkPass
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 DarkPassVS();
        PixelShader = compile ps_2_0 DarkPassPS();
    }

}

// BLEND TWO VERTEX
OutPutWScreenPos BlendTwoVS(InputVS input)
{
    OutPutWScreenPos output = (OutPutWScreenPos)0;
	float4x4 worldView = mul( g_mWorld,  g_mView);
    output.posH =  mul(input.pos, mul(worldView, g_mProj));
    output.screenPos = output.posH;
    return output;
}


//BLEND TWO PIXEL
float4 BlendTwoPS(OutPutWScreenPos input) : COLOR0
{
	float2 screenPos = ClampScreen(input.screenPos.xy);
	float4 col1 = tex2D( Blend1Sampler, screenPos); // god rays
	float4 col2 = tex2D( Blend2Sampler, screenPos ); //scene 
	if(col1.r == 0 && col1.g == 0 && col1.b == 0) // if it's plain black on god rays, return the scene color
		return col2;
	//if we are here, god rays texture has some color for this pixel		
	return  (col1*2 + col2);

	//return  (col1*2.0f + col2 * 2.0f) * (1.0f/2.0f);
}

technique BlendTwoTextures
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 BlendTwoVS();
        PixelShader = compile ps_2_0 BlendTwoPS();
    }

}

// BLENDING SUBTRACTED LIGHT WITH NOISE VS
OutPutWScreenPos BlendLightNoiseVS(InputVS input)
{
    OutPutWScreenPos output = (OutPutWScreenPos)0;
	float4x4 worldView = mul( g_mWorld,  g_mView);
    output.posH =  mul(input.pos, mul(worldView, g_mProj));
    output.screenPos = output.posH;
    return output;
}


// BLENDING SUBTRACTED LIGHT WITH NOISE PS
float4 BlendLightNoisePS(OutPutWScreenPos input) : COLOR0
{
	float2 screenPos = ClampScreen(input.screenPos.xy);
	float2 newPos = screenPos;
	newPos.y += gVal;
	float4 col1 = tex2D( RainSampler, newPos).rgba; //the noise texture
	float4 col2 = tex2D( RainSourceSampler, screenPos ); //where to apply the noise
	float4 col3 = tex2D( OrigionalLightSampler, screenPos ); //the origional light

	if(	col2.r  > gGlobalClearColor.r   ||
	    col2.r  < gGlobalClearColor.r   ||
		col2.g  > gGlobalClearColor.g   ||
	    col2.g  < gGlobalClearColor.g   ||
		col2.b  > gGlobalClearColor.b   ||
	    col2.b  < gGlobalClearColor.b )
		{
			if(col1.r+col1.g+col1.b > 0.18f)
				return col3*1.5f;//make this pixel brighter!
		}
	return col3;
}

technique BlendLightNoise
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 BlendLightNoiseVS();
        PixelShader = compile ps_2_0 BlendLightNoisePS();
    }

}

//GodRay VERTEX
OutPutWScreenPos GodRayVS(InputVS input)
{
    OutPutWScreenPos output = (OutPutWScreenPos)0;
	float4x4 worldView = mul( g_mWorld,  g_mView);
    output.posH =  mul(input.pos, mul(worldView, g_mProj));
    output.UV0 = input.uv;
	output.screenPos = output.posH;
    return output;
}


//Godray PIXEL
float4 GodRayPS(OutPutWScreenPos input) : COLOR0
{	
	float2 screenPos = ClampScreen(input.screenPos.xy);

	float2 TexCoord = screenPos;
	float2 lightPos = gLightPos;
    float2 DeltaTexCoord = ( TexCoord.xy - lightPos.xy );
	float IlluminationDecay = gIllumDecay;
    DeltaTexCoord *= 1.0 / g_mPasses * gDensity;
    float4 Color = tex2D( GodRaySampler, TexCoord );
    for( int i = 0; i < g_mPasses; ++i )
    {
        TexCoord -= DeltaTexCoord;
        float4 Sample = tex2D( GodRaySampler, TexCoord );
		float sum = Sample.r + Sample.g + Sample.b;
		if(sum > gTHold)
		{
			Sample *= IlluminationDecay * gWeight;
			Color += Sample;
			IlluminationDecay *= gDecay;
		}
		else
		{
			IlluminationDecay *= gDecay;
		}
    }
    return float4( Color * gExposure);
}

technique GodRay
{
    pass Pass1
    {
        VertexShader = compile vs_3_0 GodRayVS();
        PixelShader = compile ps_3_0 GodRayPS();
    }

}	


// Sun Shader VS VERTEX
OutputVS SunShaderVS(InputVS input)
{
    OutputVS output = (OutputVS)0;
    float4x4 worldView = mul( g_mWorld,  g_mView);
    output.posH =  mul(input.pos, mul(worldView, g_mProj));
    output.UV0 = input.uv;
    return output;
}

//Sun Shader PIXEL
float4 SunShaderPS(OutputVS input) : COLOR0
{	
	float4 col = tex2D(DiffuseSampler,input.UV0).rgba;
    return  col;
}

technique SunShader
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 SunShaderVS();
        PixelShader = compile ps_2_0 SunShaderPS();
    }

}	

// Shadow Shader VS VERTEX
OutShadowMap ShadowMappedVS(InputVS input)
{
    // Zero output
	OutShadowMap outVS = (OutShadowMap)0;
    // transform normal into world space
	outVS.normalW = normalize(mul( (float3)input.norm, (float3x3) mul(g_mWorld,g_mView) ));	
	outVS.posW = mul(input.pos, mul(g_mWorld,g_mView));
    // Transform to homogenous clip space
	outVS.posH = mul(outVS.posW, g_mProj);
    outVS.posL = mul(outVS.posW, g_mViewToLightProj);
    outVS.UV0 = input.uv;
    //return output
	return outVS;
}
struct outPS
{
	float4 col0: COLOR0;
};

//Shadow Shader PIXEL
outPS ShadowMappedPS(OutShadowMap input) 
{	
	float4 Diffuse;

    // vLight is the unit vector from the light to this pixel
    float3 vLight = normalize( float3(input.posW - gLightPosition ) );
    // Compute diffuse from the light
    if( dot( vLight, g_vLightDir ) > g_fCosTheta ) // Light must face the pixel (within Theta)
    {
        //transform from RT space to texture space.
        float2 ShadowTexC = 0.5 * input.posL.xy / input.posL.w + float2( 0.5, 0.5 );
        ShadowTexC.y = 1.0f - ShadowTexC.y;

        // transform to texel space
        float2 texelpos = SMAP_SIZE * ShadowTexC;
        // Determine the lerp amounts           
        float2 lerps = frac( texelpos );

        //read in bilerp stamp, doing the shadow checks
        float sourcevals[4];
		if(g_isMapped)
        {
			sourcevals[0] = (tex2D( ShadowSampler, ShadowTexC ) + NOISE_AMOUNT < input.posL.z / input.posL.w)? 0.0f: 1.0f;  
			sourcevals[1] = (tex2D( ShadowSampler, ShadowTexC + float2(1.0/SMAP_SIZE, 0) ) + NOISE_AMOUNT < input.posL.z / input.posL.w)? 0.0f: 1.0f;  
			sourcevals[2] = (tex2D( ShadowSampler, ShadowTexC + float2(0, 1.0/SMAP_SIZE) ) + NOISE_AMOUNT < input.posL.z / input.posL.w)? 0.0f: 1.0f;  
			sourcevals[3] = (tex2D( ShadowSampler, ShadowTexC + float2(1.0/SMAP_SIZE, 1.0/SMAP_SIZE) ) + NOISE_AMOUNT <input.posL.z / input.posL.w)? 0.0f: 1.0f;  
		}
        // lerp between the shadow values to calculate our light amount
        float LightAmount = 0.0f;
		if(g_isMapped)
		{
			LightAmount = lerp( lerp( sourcevals[0], sourcevals[1], lerps.x ),
                                lerp( sourcevals[2], sourcevals[3], lerps.x ),
                                lerps.y );
		}
		else
		{
			LightAmount = 1.0f;
		}
								  	
        // Light it
        Diffuse = ( saturate( dot( -vLight, normalize( input.normalW ) ) * LightAmount) * ( 1 - gAmbientLight ) + gDiffuseLight );
    } 
	else
    {
        Diffuse = float4(0,0,0,1);
    }

	float4 col =  tex2D(DiffuseSampler,input.UV0).rgba;
    float3 ambient = col * gAmbientLight;
    // Sum of them all together
	outPS ret;
	ret.col0 = float4(ambient + col*Diffuse, col.a);
	return  ret;
}

technique ShadowMapped
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 ShadowMappedVS();
        PixelShader = compile ps_2_0 ShadowMappedPS();
    }

}	


OutputVS ShadowVS(InputVS input)
{
	OutputVS output = (OutputVS)0;
    output.posH =  mul(input.pos, mul(g_mWorld,g_mView));
	output.posH = mul(output.posH, g_mProj);
    output.UV0 = output.posH.zw;
    return output;
}


float4 ShadowPS(OutputVS input): COLOR0
{
    return float4(1,1,1,1) * (input.UV0.x) / input.UV0.y;
}

technique ShadowTech
{
    pass p0
    {
        VertexShader = compile vs_2_0 ShadowVS();
        PixelShader = compile ps_2_0 ShadowPS();
    }

}

struct InDebugVS
{
    float4 pos : POSITION0;
    float4 col: TEXCOORD0;
};

struct OutDebugVS
{
    float4 posH : POSITION0;
    float4 col : TEXCOORD0;
};

//Standard textured rendering vertex shader
OutDebugVS DebugVS(InDebugVS input)
{
    OutDebugVS output = (OutDebugVS)0;
	output.posH = mul(input.pos, mul(mul(g_mWorld,g_mView),g_mProj));
    output.col = input.col;
    return output;
}


//textured pixel shader
float4 DebugPS(OutDebugVS input) : COLOR0
{
    return input.col;
}

technique Debug
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 DebugVS();
        PixelShader = compile ps_2_0 DebugPS();
    }
}

OutputVS DebugBlendVS(InputVS input)
{
	// Zero output
	OutputVS outVS = (OutputVS)0;
    // transform normal into world space
	outVS.posH = mul(mul(input.pos, mul(g_mWorld,g_mView)), g_mProj);
    outVS.UV0 = input.uv;
    //return output
	return outVS;
}

float4 DebugBlendPS(OutputVS input): COLOR0
{
    return float4(tex2D(DiffuseSampler,input.UV0).rgb * g_audioDebugColorMul.rgb, g_audioDebugAlpha);
}

technique DebugBlend
{
    pass Pass1
    {
		AlphaBlendEnable = true;
		DestBlend = INVSRCALPHA;
		SrcBlend = SRCALPHA;
        VertexShader = compile vs_2_0 DebugBlendVS();
        PixelShader = compile ps_2_0 DebugBlendPS();
    }
}

OutputVS SkyBoxVS(InputVS input)
{
	// Zero output
	OutputVS outVS = (OutputVS)0;
    // transform normal into world space
	outVS.posH = mul(mul(input.pos, mul(g_mWorld,g_mView)), g_mProj);
    outVS.UV0 = input.uv;
    //return output
	return outVS;
}

float4 SkyBoxPS(OutputVS input): COLOR0
{
    return tex2D(DiffuseSampler,input.UV0).rgba;
}

technique SkyBox
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 SkyBoxVS();
        PixelShader = compile ps_2_0 SkyBoxPS();
    }
}


struct InputAi
{
    float4 pos : POSITION0;
    float4 col : TEXCOORD0;
};
struct OutputAi
{
    float4 posH : POSITION0;
    float4 col : TEXCOORD0;
};

OutputAi PerceptionVS(InputAi input)
{
	// Zero output
	OutputAi outVS = (OutputAi)0;
    // transform into clip space
	outVS.posH = mul(mul(input.pos, mul(g_mWorld,g_mView)), g_mProj);
    outVS.col = input.col;
    //return output
	return outVS;
}

float4 PerceptionPS(OutputAi input): COLOR0
{
    return input.col;
}

technique Ai_Perception_Tech
{
    pass Pass1
    {
        VertexShader = compile vs_2_0 PerceptionVS();
        PixelShader = compile ps_2_0 PerceptionPS();
    }
}
